package middleware

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"server/model/common/response"
	sysService "server/service/system"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"server/global"
	"server/model/system"
	"server/service"
)

var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationLogService

func OperationRecord() gin.HandlerFunc {
	return func(c *gin.Context) {
		var body []byte
		var userId uint
		var userName string
		if c.Request.Method != http.MethodGet {
			var err error
			body, err = ioutil.ReadAll(c.Request.Body)
			if err != nil {
				global.GvaLog.Error("read body from request error:", zap.Any("err", err))
			} else {
				c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
			}
		}
		//上下文---权限获取
		permData, _ := c.Get("permData")
		permDataWait, ok := permData.(*system.SysPermission)
		if !ok {
			response.Forbidden("未获取上下文权限信息", c)
			c.Abort()
			return
		}
		var isRecordLog bool
		if c.Request.Method == "POST" || c.Request.Method == "PUT" {
			isRecordLog = true
		} else {
			if permDataWait.ID != 0 && permDataWait.IsOperationLog == true {
				isRecordLog = true
			}
		}
		//不记录日志直接跳过下一步，否则继续执行
		if !isRecordLog {
			return
		}
		//面包屑
		var operationNameArr []string
		if permDataWait.ID != 0 {
			if permBreadcrumb, err := sysService.PermissionServiceApp.GetBreadcrumbList(permDataWait.ID, false); err == nil {
				for _, permission := range permBreadcrumb {
					operationNameArr = append(operationNameArr, permission.DisplayName)
				}
			}
		}
		record := system.SysOperationLog{
			OpertionName: strings.Join(operationNameArr, "->"),
			PermId:       permDataWait.ID,
			RouteName:    permDataWait.Name,
			Url:          c.Request.URL.String(),
			Ip:           c.ClientIP(),
			Method:       c.Request.Method,
			Agent:        c.Request.UserAgent(),
			Body:         string(body),
		}
		writer := responseBodyWriter{
			ResponseWriter: c.Writer,
			body:           &bytes.Buffer{},
		}
		c.Writer = writer
		now := time.Now()
		//提前执行控制器方法，可以收集到登录状态下登录成功的用户登录日志
		c.Next()
		//上下文---用户获取
		claims, ok := c.Get("claims")
		if ok {
			if waitUse, ok := claims.(*sysService.CustomClaims); ok {
				userId = waitUse.ID
				userName = waitUse.Name
			} else {
				return
			}
		}
		record.UserId = userId
		record.UserName = userName

		latency := time.Since(now)
		record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
		record.Status = c.Writer.Status()
		record.Latency = latency
		record.Resp = writer.body.String()
		//异步写入日志
		go operationRecordService.CreateSysOperationLog(record)
		/*if err := operationRecordService.CreateSysOperationLog(record); err != nil {
			global.GvaLog.Error("create operation record error:", zap.Any("err", err))
		}*/
	}
}

type responseBodyWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (r responseBodyWriter) Write(b []byte) (int, error) {
	r.body.Write(b)
	return r.ResponseWriter.Write(b)
}
